
#include "include/types.h"
#include "include/riscv.h"
#include "include/param.h"
#include "include/memlayout.h"
#include "include/spinlock.h"
#include "include/proc.h"
#include "include/syscall.h"
#include "include/timer.h"
#include "include/kalloc.h"
#include "include/string.h"
#include "include/printf.h"
#include "include/vm.h"   

extern int exec(char *path, char **argv);

uint64
sys_exec(void)
{
  char path[FAT32_MAX_PATH], *argv[MAXARG];
  int i;
  uint64 uargv, uarg;

  if(argstr(0, path, FAT32_MAX_PATH) < 0 || argaddr(1, &uargv) < 0){
    return -1;
  }
  memset(argv, 0, sizeof(argv));
  for(i=0;; i++){
    if(i >= NELEM(argv)){
      goto bad;
    }
    if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){
      goto bad;
    }
    if(uarg == 0){
      argv[i] = 0;
      break;
    }
    argv[i] = kalloc();
    if(argv[i] == 0)
      goto bad;
    if(fetchstr(uarg, argv[i], PGSIZE) < 0)
      goto bad;
  }

  int ret = exec(path, argv);

  for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
    kfree(argv[i]);

  return ret;

 bad:
  for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
    kfree(argv[i]);
  return -1;
}

uint64
sys_exit(void)
{
  int n;
  if(argint(0, &n) < 0)
    return -1;
  exit(n);
  return 0; 
}

uint64
sys_getpid(void)
{
  return myproc()->pid;
}

uint64
sys_fork(void)
{
  return fork();
}

uint64
sys_wait(void)
{
  uint64 p;
  if(argaddr(1, &p) < 0)
    return -1;

  return wait(p);
}

uint64
sys_sbrk(void)
{
  int addr;
  int n;

  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;
  if(growproc(n) < 0)
    return -1;
  return addr;
}

uint64
sys_sleep(void)
{
  int n;
  uint ticks0;

  if(argint(0, &n) < 0)
    return -1;


  acquire(&tickslock);
  ticks0 = ticks;
  while(ticks - ticks0 < n){
    if(myproc()->killed){
      release(&tickslock);
      return -1;
    }
	
    sleep(&ticks, &tickslock);
  }
  release(&tickslock);
  return 0;
}

uint64
sys_kill(void)
{
  int pid;

  if(argint(0, &pid) < 0)
    return -1;
  return kill(pid);
}
uint64
sys_uptime(void)
{
  uint xticks;

  acquire(&tickslock);
  xticks = ticks;
  release(&tickslock);
  return xticks;
}

uint64
sys_trace(void)
{
  int mask;
  if(argint(0, &mask) < 0) {
    return -1;
  }
  myproc()->tmask = mask;
  return 0;
}


uint64
sys_execve(void)
{
  char path[FAT32_MAX_PATH], *argv[MAXARG];
  uint64 uargv, uarg;
  uint64 uenviron, uenv;
  
  int i;
  
  if(argstr(0, path, 260) < 0 || argaddr(1, &uargv) < 0 || argaddr(2, &uenviron) < 0)
  {
    return -1;
  }
  
  memset(argv, 0, sizeof(argv));
  
  uenv = 0;
  uenv++; 
  
  for(i=0;; i++)
  {
    if(i >= NELEM(argv)){
      goto bad;
    }
	
    if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){
      goto bad;
    }
	
    if(uarg == 0){
      argv[i] = 0;
      break;
    }
	
    argv[i] = kalloc();
    if(argv[i] == 0)
      goto bad;
    if(fetchstr(uarg, argv[i], PGSIZE) < 0)
      goto bad;
  
  }

  int ret = exec(path, argv);

  for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
    kfree(argv[i]);

  return ret;

 bad:
  for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
    kfree(argv[i]);
  return -1;
}

uint64
sys_brk(void)
{
  int addr;
  int n;

  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;
  if(growproc(n) < 0)
    return -1;
  return addr;
}

uint64
sys_get_time(void)
{
  uint xticks;

  acquire(&tickslock);
  xticks = ticks / 10;
  release(&tickslock);

  return xticks;
}

uint64
sys_nanosleep(void)
{
  int n;
  uint ticks0;

  if(argint(0, &n) < 0)
    return -1;

  acquire(&tickslock);
  ticks0 = ticks;
  while(ticks - ticks0 < n){
    if(myproc()->killed){
      release(&tickslock);
      return -1;
    }
    sleep(&ticks, &tickslock);
  }
  release(&tickslock);
  return 0;
}

uint64
sys_runtest(void)
{
  char *argv1[] = { "brk", 0 };
  char *argv2[] = { "chdir", 0 };
  char *argv3[] = { "close", 0 };
  char *argv4[] = { "dup", 0 };
  char *argv5[] = { "dup2", 0 };
  char *argv6[] = { "execve", 0 };
  char *argv7[] = { "fork", 0 };
  char *argv8[] = { "fstat", 0 };
  char *argv9[] = { "getcwd", 0 };
  char *argv10[] = { "getdents", 0 };
  char *argv11[] = { "getpid", 0 };
  char *argv12[] = { "mkdir_", 0 };
  char *argv13[] = { "open", 0 };
  char *argv14[] = { "openat", 0 };
  char *argv15[] = { "pipe", 0 };
  char *argv16[] = { "read", 0 };
  char *argv17[] = { "uname", 0 };
  char *argv18[] = { "unlink", 0 };
  char *argv19[] = { "write", 0 };
  char *argv20[] = { "wait", 0 };
  char *argv21[] = { "exit", 0 };
  
  fork();
  exec("close", argv3);
  
  fork();
  exec("brk", argv1);
	
  fork();
  exec("chdir", argv2);
		
  fork();
  exec("dup", argv4);
  
  fork();
  exec("dup2", argv5);
	
  fork();
  exec("fork", argv7);
	
  fork();
  exec("fstat", argv8);
  
  fork();
  exec("getcwd", argv9);
	
  fork();
  exec("getdents", argv10);
	
  fork();
  exec("getpid", argv11);
	
  fork();
  exec("mkdir_", argv12);
  
  fork();
  exec("openat", argv14);
  
  fork();
  exec("open", argv13); 
  	
  fork();
  exec("pipe", argv15);
	
  fork();
  exec("read", argv16);
  
  fork();
  exec("uname", argv17);
	
  fork();
  exec("unlink", argv18);
	
  fork();
  exec("write", argv19);

  fork();
  exec("wait", argv20);

  fork();
  exec("exit", argv21);
     
  fork();
  exec("execve", argv6);

  return 0;
}
